package net.qiqbframework.eventhandling.gateway;

import net.qiqbframework.common.Registration;
import net.qiqbframework.eventhandling.EventBus;
import net.qiqbframework.eventhandling.EventMessage;
import net.qiqbframework.messaging.intercepting.MessageDispatchInterceptor;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static java.util.Arrays.asList;
import static net.qiqbframework.common.BuilderUtils.assertNonNull;
import static net.qiqbframework.eventhandling.GenericEventMessage.asEventMessage;

public abstract class AbstractEventGateway implements EventGateway {

    private final EventBus eventBus;
    private final List<MessageDispatchInterceptor<? super EventMessage<?>>> dispatchInterceptors;


    protected AbstractEventGateway(Builder builder) {
        builder.validate();
        this.eventBus = builder.eventBus;
        this.dispatchInterceptors = builder.dispatchInterceptors;
    }

    @Override
    public void publish(@Nonnull Object event) {
        this.eventBus.publish(processInterceptors(asEventMessage(event)));
    }


    public Registration registerDispatchInterceptor(@Nonnull MessageDispatchInterceptor<? super EventMessage<?>> interceptor) {
        dispatchInterceptors.add(interceptor);
        return () -> dispatchInterceptors.remove(interceptor);
    }

    /**
     * Invokes all the dispatch interceptors and returns the EventMessage instance that should be dispatched.
     *
     * @param eventMessage The incoming event message
     * @return The event message to dispatch
     */
    @SuppressWarnings("unchecked")
    protected <E> EventMessage<? extends E> processInterceptors(EventMessage<E> eventMessage) {
        EventMessage<? extends E> message = eventMessage;
        for (MessageDispatchInterceptor<? super EventMessage<?>> dispatchInterceptor : dispatchInterceptors) {
            message = (EventMessage) dispatchInterceptor.handle(message);
        }
        return message;
    }

    /**
     * Returns the EventBus used by this EventGateway. Should be used for monitoring or testing.
     *
     * @return the EventBus used by this gateway.
     */
    public EventBus getEventBus() {
        return eventBus;
    }


    public abstract static class Builder {

        private EventBus eventBus;
        private List<MessageDispatchInterceptor<? super EventMessage<?>>> dispatchInterceptors =
                new CopyOnWriteArrayList<>();


        public Builder eventBus(@Nonnull EventBus eventBus) {
            assertNonNull(eventBus, "EventBus may not be null");
            this.eventBus = eventBus;
            return this;
        }


        public Builder dispatchInterceptors(
                MessageDispatchInterceptor<? super EventMessage<?>>... dispatchInterceptors) {
            return dispatchInterceptors(asList(dispatchInterceptors));
        }

        public Builder dispatchInterceptors(
                List<MessageDispatchInterceptor<? super EventMessage<?>>> dispatchInterceptors) {
            this.dispatchInterceptors = dispatchInterceptors != null && !dispatchInterceptors.isEmpty()
                    ? new CopyOnWriteArrayList<>(dispatchInterceptors)
                    : new CopyOnWriteArrayList<>();
            return this;
        }

        protected void validate() {
            assertNonNull(eventBus, "The EventBus is a hard requirement and should be provided");
        }
    }
}
